/**
 * DMonImpl.java
 *
 * @author Alex Tough (07-41604)
 * @author Carlos Colmenares (06-39380)
 *
 * Implementación de una interfaz remota de DMon
 */

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

public class DMonImpl extends UnicastRemoteObject implements DMon {

    static final long serialVersionUID = 1234123412345L;
   
    /**
     * Esta variable indica el puerto por el cual el DMon
     * estará asociado
     */
    private int puertoDmon;

    /**
     * Cuando esta variable es verdadera, el programa imprime
     * por pantalla detalles del estado de la corrida
     */
    private boolean verboso;

    //Set<String> registroDeUsuarios =
    //    Collections.synchronizedSet(new HashSet<String>());

    /**
     * Esta estructura contendrá la base de datos de usuarios y
     * DRep conectados a este DMon, las claves son los identificadores
     * de un DRep y el resultado es un Set con los usuarios que
     * están conectados a ese DRep
     */
    Map<String,Set<String> > registroDRep;

    /**
     * El número de UPloads realizados por usuarios
     * conectados a DReps conectados a este DMon
     */
    public int uploads;

    /**
     * El número de DOWNloads realizados por usuarios
     * conectados a DReps conectados a este DMon
     */
    public int downloads;

    /**
     * Construye la clase con los argumentos provistos, también
     * hace las validaciones necesarias de dichos argumentos y reporta
     * errores.
     * @param args Los argumentos por línea de comando con los que se
     *        inicializará el proceso DMon
     */
    public DMonImpl(String[] args) throws RemoteException {
        super();
        this.registroDRep = new HashMap<String,Set<String> >();
        this.uploads = 0;
        this.downloads = 0;

        parsearArgumentos(args);
    }

    /**
     * Parsea argumentos y pone valores por default si es necesario
     * @param args Los arumentos por línea de comando
     */
    private void parsearArgumentos(String[] args){
        //los siguientes son valores de default para
        //estos:
        this.puertoDmon = 0;
        this.verboso = false;

        try {

            int i = 0;
            while( i < args.length ){

                if( args[i].equals("-p") && i+1<args.length ){
                    puertoDmon = Integer.parseInt(args[++i]);
                }
                else if( args[i].equals("-v") ){
                    verboso = true;
                }
                else { //( args[i].equals("--help") )
                    imprimirUso();
                }
                i++;
            }
            if( puertoDmon == 0 )
                imprimirUso();
            if(puertoDmon > 65535 || puertoDmon < 0){
                System.out.println("Error, especifique un número de puerto"+
                        "válido");
                System.exit(-1);
            }
        }
        catch(NumberFormatException nfe){
            System.err.println("Error obteniendo número de puertos, por "+
                    "favor indique números enteros");
            System.exit(1);
        }

    }

    /**
     * Esta función imprime los flags que se deben proporcionar
     * al programa para su correto funcionamiento
     */
    private static void imprimirUso(){
        String mnsjError =
            "uso: dmon -p <puerto local dmon> [-v]";
        System.out.println(mnsjError);
        System.exit(1);
    }

    /**
     * Retorna el puerto en donde se localiza el DMon
     * @return El puerto en donde funciona el DMon
     */
    public int getPuertoDMon(){
        return this.puertoDmon;
    }

    /**
     * Retorna el valor de la variable verboso
     * @return Retorna un valor, de ser cierto, el DMon está en
     *         modo verboso
     */
    public boolean isVerboso(){
        return this.verboso;
    }

    /**
     * Retorna el número de uploads hechos por los DCLI
     * conectados a DRep conectados a este DMon
     * @return El número de Uploads hechos por DReps
     *         aliados a este DMon
     */
    public int getUploads(){
        return this.uploads;
    }

    /**
     * Retorna el número de downloads hechos por los DCLI
     * conectados a DRep conectados a este DMon
     * @return El número de Downloads hechos por DReps
     *         aliados a este DMon
     */
    public int getDownloads(){
        return this.downloads;
    }

    //...............................................................
    // Implementaciones
    //...............................................................

    public void subir() throws RemoteException{
        uploads++;
    }

    public void bajar() throws RemoteException {
        downloads++;
    }

    public String[] tuser() throws RemoteException {
        Vector<String> v = new Vector<String>();
        Set<String> keys = registroDRep.keySet();
        
        //Iterar por cada DRep registrado
        Iterator<String> i = keys.iterator();
        while(i.hasNext()){
            String drep = i.next();

            //Iterar por cada usuario conectado al DRep
            Set<String> usuarios = registroDRep.get(drep);
            Iterator<String> j = usuarios.iterator();

            while(j.hasNext()){
                String usuario = j.next();
                v.add(usuario+" "+drep);
            }
            
        }

        return v.toArray(new String[0]);
    }
    
    public int nrep() throws RemoteException {
        return registroDRep.size();
    }
    
    public void registrarDRep(String nombreDeDRep)
        throws RemoteException, DMonException
    {
        if(registroDRep.containsKey(nombreDeDRep)){
            throw new DMonException("Ya existe un DRep registrado bajo"+
                    " ese nombre");
        }
        registroDRep.put(nombreDeDRep,new HashSet<String>());
    }
    
    public void registrarDCLI(String nombreDeUsuario, String nombreDeDRep)
        throws RemoteException, DMonException
    {
        if(!registroDRep.containsKey(nombreDeDRep)){
            throw new DMonException("No existe un DRep registrado bajo"+
                    " ese nombre");
        }
        if(registroDRep.get(nombreDeDRep).contains(nombreDeUsuario)){
            throw new DMonException("Ya existe un Usuario registrado bajo"+
                    " ese nombre en el DRep indicado");
        }
        registroDRep.get(nombreDeDRep).add(nombreDeUsuario);
    }
    
    public void desconectarDRep(String nombreDeDRep)
        throws RemoteException, DMonException
    {
        if(!registroDRep.containsKey(nombreDeDRep)){
            throw new DMonException("DRep no existente");
        }
        registroDRep.remove(nombreDeDRep);
    }

    public void desconectarDCLI(String nombreDeUsuario, String nombreDeDRep)
        throws RemoteException, DMonException    
    {
        if(!registroDRep.containsKey(nombreDeDRep)){
            throw new DMonException("DRep no existente");
        }
        if(!registroDRep.get(nombreDeDRep).contains(nombreDeUsuario)){
            throw new DMonException("Usuario no existente en DRep");
        }
        registroDRep.get(nombreDeDRep).remove(nombreDeUsuario);
    }
}
